﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;

namespace PicGame.Utils
{
    internal sealed class PicGameMain
    {
        private bool _isInited = false;                                                         /*标志是否调用过Init方法*/
        private int _rowCount, _colCount;                                                       /*rowCount：行数， yCount：列数*/
        private float _imgWidth, _imgHeight;                                                    /*图片的宽高*/
        private float _subImgWidth, _subImgHeight;                                              /*小方块、小图片的宽高*/
        private int[,] _imgIndex;                                                               /*与图片对应的索引*/
        private Bitmap[] _bmps;                                                                 /*经过程序切割后的小图片数组*/
        private RectangleF[,] _rectFs;                                                          /*显示图片的小方块区域数组*/
        private Stack<HistoryItem> _historyStack = new Stack<HistoryItem>();                    /*记录游戏的每一步的数据*/
        private Pen _pen = new Pen(Color.Black);                                                /*用于画线*/
        private PointF _firstP;                                                                 /*在picturebox画图的起始位置*/
        private Color _lastColor = Color.FromArgb(212, 208, 200);                               /*空白图片的颜色*/
        private Random _rand = new Random();

        private PicGameMain()
        {

        }

        private static PicGameMain _instance;

        public static PicGameMain Instance
        {
            get
            {
                if (PicGameMain._instance == null)
                {
                    PicGameMain._instance = new PicGameMain();
                }

                return PicGameMain._instance;
            }
        }

        /// <summary>
        ///返回游戏总共经过的步数 
        /// </summary>
        public int GameStepCount => this._historyStack.Count;

        /// <summary>
        /// 初始化游戏
        /// </summary>
        /// <param name="rowCount"></param>
        /// <param name="colCount"></param>
        /// <param name="mainBmp"></param>
        /// <param name="firstP"></param>
        /// <param name="action"></param>
        public void Init(int rowCount, int colCount, Bitmap mainBmp, PointF firstP, Action action)
        {
            try
            {
                this._rowCount = rowCount;
                this._colCount = colCount;
                this._firstP = firstP;
                this._imgWidth = mainBmp.Width;
                this._imgHeight = mainBmp.Height;
                this._imgIndex = this.GetRandArr(this._rowCount, this._colCount);
                this._bmps = this.CutImage(mainBmp, this._rowCount, this._colCount, out this._subImgWidth, out this._subImgHeight);
                this._rectFs = this.InitRectFs(this._rowCount, this._colCount);
                this._historyStack.Clear();

                if (action != null)
                {
                    action.Invoke();
                }

                this._isInited = true;
            }
            catch
            {
                this._isInited = false;
            }
        }

        /// <summary>
        /// 游戏退回上一步
        /// </summary>
        /// <param name="action"></param>
        public void GameBack(Action<HistoryItem> action)
        {
            if (this._historyStack.Count > 0)
            {
                var item = this._historyStack.Pop();

                this.Swap(ref this._imgIndex[item.P1.X, item.P1.Y], ref this._imgIndex[item.P2.X, item.P2.Y]);

                if (action != null)
                {
                    action.Invoke(item);
                }
            }
        }

        /// <summary>
        /// 判断游戏是否结束
        /// </summary>
        /// <returns></returns>
        public bool IsGameOver()
        {
            if (this._isInited)
            {
                for (int i = 0; i < this._rowCount; i++)
                {
                    for (int j = 0; j < this._colCount; j++)
                    {
                        if (this._imgIndex[i, j] != this._colCount * i + j)
                        {
                            return false;
                        }
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 用于鼠标操作时移动对应的图片
        /// </summary>
        /// <param name="p"></param>
        /// <param name="action"></param>
        public void MoveSubImg(Point p, Action<HistoryItem> action)
        {
            if (!this._isInited)
            {
                return;
            }

            int max = this._rowCount * this._colCount - 1;

            for (int i = 0; i < this._rowCount; i++)
            {
                for (int j = 0; j < this._colCount; j++)
                {
                    if (this._rectFs[i, j].Contains(p))
                    {
                        if (i - 1 >= 0 && this._imgIndex[i - 1, j] == max)
                        {
                            this.MoveSubImg(FX.SHANG, action);
                        }
                        else if (i + 1 <= this._rowCount - 1 && this._imgIndex[i + 1, j] == max)
                        {
                            this.MoveSubImg(FX.XIA, action);
                        }
                        else if (j - 1 >= 0 && this._imgIndex[i, j - 1] == max)
                        {
                            this.MoveSubImg(FX.ZUO, action);
                        }
                        else if (j + 1 <= this._colCount - 1 && this._imgIndex[i, j + 1] == max)
                        {
                            this.MoveSubImg(FX.YOU, action);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 用于键盘操作时移动对应的图片
        /// </summary>
        /// <param name="fx"></param>
        /// <param name="action"></param>
        public void MoveSubImg(FX fx, Action<HistoryItem> action)
        {
            if (this._isInited)
            {
                var rectF = RectangleF.Empty;
                int max = this._rowCount * this._colCount - 1;
                int r = -1, c = -1;
                int r_n = 0, c_n = 0;
                bool isExec = false;

                /*找出最后一个数字所在的位置（空白图片所在的位置）*/
                for (int i = 0; i < this._rowCount; i++)
                {
                    for (int j = 0; j < this._colCount; j++)
                    {
                        if (this._imgIndex[i, j] == max)
                        {
                            r = i;
                            c = j;
                            break;
                        }
                    }
                }

                switch (fx)
                {
                    case FX.SHANG:
                        if (r + 1 <= this._rowCount - 1)
                        {
                            rectF = new RectangleF(
                                this._firstP.X + c * this._subImgWidth,
                                this._firstP.Y + r * this._subImgHeight,
                                this._subImgWidth,
                                this._subImgHeight * 2
                            );

                            r_n = r + 1;
                            c_n = c;
                            isExec = true;
                        }
                        break;
                    case FX.XIA:
                        if (r - 1 >= 0)
                        {
                            rectF = new RectangleF(
                                this._firstP.X + c * this._subImgWidth,
                                this._firstP.Y + (r - 1) * this._subImgHeight,
                                this._subImgWidth,
                                this._subImgHeight * 2
                            );

                            r_n = r - 1;
                            c_n = c;
                            isExec = true;
                        }
                        break;
                    case FX.ZUO:
                        if (c + 1 <= this._colCount - 1)
                        {
                            rectF = new RectangleF(
                                this._firstP.X + c * this._subImgWidth,
                                this._firstP.Y + r * this._subImgHeight,
                                this._subImgWidth * 2,
                                this._subImgHeight
                            );

                            r_n = r;
                            c_n = c + 1;
                            isExec = true;
                        }
                        break;
                    case FX.YOU:
                        if (c - 1 >= 0)
                        {
                            rectF = new RectangleF(
                                this._firstP.X + (c - 1) * this._subImgWidth,
                                this._firstP.Y + r * this._subImgHeight,
                                this._subImgWidth * 2,
                                this._subImgHeight
                            );

                            r_n = r;
                            c_n = c - 1;
                            isExec = true;
                        }
                        break;
                }

                if (isExec)
                {
                    var item = new HistoryItem(rectF, new Point(r, c), new Point(r_n, c_n));

                    this._historyStack.Push(item);

                    this.Swap(ref this._imgIndex[r, c], ref this._imgIndex[r_n, c_n]);

                    if (action != null)
                    {
                        action.Invoke(item);
                    }
                }
            }
        }

        /// <summary>
        /// 绘制小图片及间隔线
        /// </summary>
        /// <param name="g"></param>
        public void PaintSubImgs(Graphics g)
        {
            if (!this._isInited)
            {
                return;
            }

            float x = this._firstP.X, y = this._firstP.Y;

            for (int i = 0; i < this._rowCount; i++)
            {
                for (int j = 0; j < this._colCount; j++)
                {
                    g.DrawImage(_bmps[this._imgIndex[i, j]], this._rectFs[i, j]);
                }
            }

            for (int i = 0; i <= this._rowCount; i++)
            {
                g.DrawLine(this._pen, x, y + i * this._subImgHeight, x + this._imgWidth, y + i * this._subImgHeight);
            }

            for (int i = 0; i <= this._colCount; i++)
            {
                g.DrawLine(this._pen, x + i * this._subImgWidth, y, x + i * this._subImgWidth, y + this._imgHeight);
            }
        }

        public Bitmap GetMinImage2(Bitmap bmp, float w, float h, int rowCount, int colCount)
        {
            var min_bmp = this.GetMinImage(bmp, w, h);
            var sub_H = (float)(min_bmp.Height - 1) / (float)rowCount;
            var sub_W = (float)(min_bmp.Width - 1) / (float)colCount;

            using (var g = Graphics.FromImage(min_bmp))
            {
                g.FillRectangle(new SolidBrush(this._lastColor), sub_W * (colCount - 1), sub_H * (rowCount - 1), sub_W, sub_H);

                for (int i = 0; i <= this._rowCount; i++)
                {
                    g.DrawLine(this._pen, 0, i * sub_H, min_bmp.Width, i * sub_H);
                }

                for (int i = 0; i <= this._colCount; i++)
                {
                    g.DrawLine(this._pen, i * sub_W, 0, i * sub_W, min_bmp.Height);
                }
            }

            return min_bmp;
        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="bmp">原图</param>
        /// <param name="w">宽度</param>
        /// <param name="h">高度</param>
        /// <returns></returns>
        public Bitmap GetMinImage(Bitmap bmp, float w, float h)
        {
            SizeF size = bmp.Size;

            while (size.Width > w || size.Height > h)
            {
                size.Width /= 1.01F;
                size.Height /= 1.01F;
            }

            var nbmp = new Bitmap(Convert.ToInt32(size.Width), Convert.ToInt32(size.Height));

            using (var g = Graphics.FromImage(nbmp))
            {
                g.DrawImage(bmp, 0, 0, size.Width, size.Height);
                return nbmp;
            }
        }

        private void Swap(ref int x, ref int y)
        {
            int t = x; x = y; y = t;
        }

        /// <summary>
        /// 按照一定的规律打乱数组
        /// </summary>
        /// <param name="rowCount"></param>
        /// <param name="colCount"></param>
        /// <returns></returns>
        private int[,] GetRandArr(int rowCount, int colCount)
        {
            int temp;
            int r = rowCount - 1;/*所在行*/
            int c = colCount - 1;/*所在列*/
            int[,] arr = new int[rowCount, colCount];

            for (int i = 0; i < rowCount; i++)
            {
                for (int j = 0; j < colCount; j++)
                {
                    arr[i, j] = colCount * i + j;
                }
            }

            /*将数组按一定的规律打乱*/
            for (int i = 0; i < rowCount * colCount * 100;)
            {
                switch (this._rand.Next(1, 5))
                {
                    case 1:/*上*/
                        temp = r - 1;
                        if (temp >= 0)
                        {
                            this.Swap(ref arr[r, c], ref arr[temp, c]);
                            r = temp;
                            i++;
                        }
                        break;
                    case 2:/*下*/
                        temp = r + 1;
                        if (temp <= this._rowCount - 1)
                        {
                            this.Swap(ref arr[r, c], ref arr[temp, c]);
                            r = temp;
                            i++;
                        }
                        break;
                    case 3:/*左*/
                        temp = c - 1;
                        if (temp >= 0)
                        {
                            this.Swap(ref arr[r, c], ref arr[r, temp]);
                            c = temp;
                            i++;
                        }
                        break;
                    case 4:/*右*/
                        temp = c + 1;
                        if (temp <= this._colCount - 1)
                        {
                            this.Swap(ref arr[r, c], ref arr[r, temp]);
                            c = temp;
                            i++;
                        }
                        break;
                }
            }

            return arr;
        }

        /// <summary>
        /// 将所给的图片切割成x * y的小图片
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="r"></param>
        /// <param name="c"></param>
        /// <param name="w"></param>
        /// <param name="h"></param>
        /// <returns></returns>
        private Bitmap[] CutImage(Bitmap bmp, int r, int c, out float w, out float h)
        {
            w = (float)bmp.Width / (float)c;
            h = (float)bmp.Height / (float)r;
            var bmps = new Bitmap[r * c];

            for (int i = 0; i < r; i++)
            {
                for (int j = 0; j < c; j++)
                {
                    bmps[i * c + j] = new Bitmap((int)w, (int)h, PixelFormat.Format32bppArgb);

                    using (var g = Graphics.FromImage(bmps[i * c + j]))
                    {
                        if (i == r - 1 && j == c - 1)
                        {
                            g.Clear(this._lastColor);
                        }
                        else
                        {
                            g.DrawImage(bmp, new RectangleF(0, 0, w, h), new RectangleF(w * j, h * i, w, h), GraphicsUnit.Pixel);
                        }
                    }
                }
            }

            return bmps;
        }

        /// <summary>
        /// 初始化所有画小图片的矩形
        /// </summary>
        /// <param name="rowCount"></param>
        /// <param name="colCount"></param>
        /// <returns></returns>
        private RectangleF[,] InitRectFs(int rowCount, int colCount)
        {
            var rectFs = new RectangleF[rowCount, colCount];
            var x = this._firstP.X;
            var y = this._firstP.Y;

            for (var i = 0; i < rowCount; i++)
            {
                for (int j = 0; j < colCount; j++)
                {
                    rectFs[i, j] = new RectangleF(
                        new PointF(x + j * this._subImgWidth, y + i * this._subImgHeight),
                        new SizeF(this._subImgWidth, this._subImgHeight));
                }
            }

            return rectFs;
        }
    }
}